import 'leaflet'
import { type VNode } from 'vue'
import { render , nextTick } from 'vue'
import HeatMap from '../../Heatmap/index'
import { type StoreData } from '../../Heatmap/index'
import { yesBtnSvg, loadingBtnSvg } from '../../const'
import { xy, getRatio, getCenterPoint, getDisance } from './LeafletUtil'

export { xy } from './LeafletUtil' //这么写主要是为了外层使用时，都从FhcLeaflet

//这里可以扩展Map的相关属性方法，以避免在代码里提示错误，相当于.d.ts
declare module 'leaflet' {
  export type BounceOptions = {
    duration?: number
    height?: number
    loop?: number
  }

  export interface MarkerOptions {
    bounceOnAdd?: boolean
    bounceOnAddOptions?: BounceOptions
    bounceOnAddCallback?: () => void
  }

  export interface Marker {
    bounce(callback?: () => void): void
    bounce(options: BounceOptions, callback?: () => void): void
    stopBounce(): void
  }

  //.....Need more
  export interface MotionLine {
    stop(): void
  }

  export interface MoveMarker extends Marker {
    addMoreLine(latLng: any, options: any): void
    getCurrentPolyline(): MotionLine
    getMarker(): MoveMarker
  }
}

import * as L from 'leaflet'
export function initMap(mapDivId: string, options?: L.MapOptions): L.Map | null {
  let map: L.Map | null = null
  try {
    const defaultOptions: L.MapOptions = {}
    const mapOptions =
      options === undefined ? defaultOptions : Object.assign(defaultOptions, options)
    map = L.map(mapDivId, mapOptions)
    const layoutattribution = document.getElementsByClassName(
      'leaflet-control-attribution leaflet-control'
    )[0] as HTMLElement
    L.DomUtil.remove(layoutattribution)
  } catch (error) {
    console.error('initMap', error)
    return null
  }
  return map
}

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>   ImageTextMarker   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export interface ImageTextOptions {
  key: number //用于区分maker内容
  mkImage: string //图片路径
  mkImageWH: number // 图片长宽（目前支持等宽高）
  mkText?: string | undefined //文字内容
  mkFontSize?: number | undefined //文字大小,不建议太长，一般和image宽度差不多比较合适
}

export class ImageTextMarker extends L.Marker {
  _options?: L.DivIconOptions
  imageTextOptions: ImageTextOptions

  constructor(
    latlng: L.LatLngExpression | L.LatLng | L.LatLngTuple,
    imageTextOptions: ImageTextOptions,
    options?: L.DivIconOptions,
    map?: L.Map
  ) {
    super(latlng, options)
    this._options = options
    this.imageTextOptions = imageTextOptions
    this.setIcon(L.divIcon(options))
    if (map) {
      this.addTo(map)
    }
  }
}

/**
 * 通过设置DivIcon的html，生成一个上图下文字的marker，并通过fitIconSize动态设置marker的area
 * @param option ImageTextOptions
 * @param latlng L.LatLngExpression
 * @param map L.Map
 * @returns obj:{ marker: ImageTextMarker, className:string}
 */
export function getImageTextMarker(
  option: ImageTextOptions,
  latlng: L.LatLngExpression,
  map: L.Map
): { marker: ImageTextMarker; className: string } {
  const maxMarkerH: number = 20
  let html =
    `<div style="display: flex;flex-direction:column;justify-content: center;align-items: center;" >` +
    `   <div style="width:${option.mkImageWH}px;height:${option.mkImageWH}px;">` +
    `     <img id="img_mark_${option.key}" src="${option.mkImage}"  style="width:${option.mkImageWH}px;height:${option.mkImageWH}px;filter: drop-shadow(0px 0px 4px white);"/></img>` +
    `   </div>`
  if (option.mkText) {
    html =
      html +
      ` <div id="txt_mark_${option.key}" style="text-shadow: 1px 1px 2px white, -1px 1px 2px white, 1px -1px 2px white, -1px -1px 2px white; color: black;text-align: center;font-weight: bold;font-size:${option.mkFontSize}px;"><span style="display: inline-block;">${option.mkText}</span> </div>`
  }
  html = html + `</div>`
  const className = 'ImageTextMarker_' + option.key
  const iconSize: L.PointExpression = [maxMarkerH, maxMarkerH]
  const divIcon: L.DivIconOptions = {
    html: html,
    className: className,
    iconSize: iconSize,
    iconAnchor: [iconSize[0] / 2, iconSize[1] / 2]
  }

  const marker = new ImageTextMarker(latlng, option, divIcon)
  marker.addTo(map)
  fitImageTextSize(marker)
  return { marker: marker, className: className }
}

/**
 * 根据marker的image和text动态设置marker的area
 * @param marker :ImageTextMarker
 */
function fitImageTextSize(marker: ImageTextMarker): void {
  setTimeout(() => {
    let wMax = 0
    let hMax = 0
    const imageTextOptions = marker.imageTextOptions
    let iconElements = document.getElementById('img_mark_' + imageTextOptions.key)
    if (iconElements && iconElements.getBoundingClientRect()) {
      wMax = iconElements.getBoundingClientRect().width
      hMax = iconElements.getBoundingClientRect().height
      iconElements = document.getElementById('txt_mark_' + imageTextOptions.key)
      if (iconElements) {
        if (iconElements.getBoundingClientRect().width > wMax)
          wMax = iconElements.getBoundingClientRect().width
        hMax = hMax + iconElements.getBoundingClientRect().height
      }
      const iconDiv = marker.getIcon()
      const iconSize: L.PointExpression = [wMax, hMax]
      const iconAnchor: L.PointExpression = [wMax / 2, hMax / 2]
      iconDiv.options.iconSize = iconSize
      iconDiv.options.iconAnchor = iconAnchor
      marker.setIcon(iconDiv) // 一定要加这句
      const className = 'ImageTextMarker_' + imageTextOptions.key
      const iconEles: HTMLCollectionOf<Element> = document.getElementsByClassName(className)
      Array.prototype.forEach.call(iconEles, (element) => {
        element.style.width = wMax + 'px'
        element.style.height = hMax + 'px'
      })
    }
  }, 0)
}

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<   ImageTextMarker   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

export interface MapObj {
  mapImgW: number
  mapImgH: number
  map: L.Map | null
}
export interface HeatPoint {
  x: number
  y: number
  value: number
}
/**
 * 根据平面图层动态计算缩放范围,最大：放大2倍,最小：能正好完整显示平面图
 * 地图尺寸 < 地图容器时，zoom level 肯定是0~n
 * 地图尺寸 > 地图容器时，zoom level 肯定是-n~2
 * @param map
 * @param imageOverlay  平面地图
 * @param paddingMap 地图显示空白范围
 * @returns
 */
export function onResetMapZoomRange(
  map: L.Map,
  imageOverlay: L.ImageOverlay | null,
  paddingMap?: number
) {
  let PaddingMap = 100
  if (paddingMap && paddingMap > 0) {
    PaddingMap = paddingMap
  }
  if (!map || !imageOverlay) return
  const rect = map.getContainer().getBoundingClientRect()
  let mapDivW = rect.width - 2 * PaddingMap
  let mapDivH = rect.height - 2 * PaddingMap
  
  let minZoom = -3
  if (imageOverlay && imageOverlay.getElement()) {
    let mapImageW = map.imageMapWidth
    let mapImageH = map.imageMapHeight
    const radio = getRatio()
    if (mapImageW * radio < mapDivW && mapImageH * radio < mapDivH) {
      let scaleW = 0
      while (mapImageW * radio * 2 < mapDivW) {
        mapImageW = mapImageW * 2
        scaleW = scaleW + 1
      }
      let scaleH = 1
      while (mapImageH * radio * 2 < mapDivH) {
        mapImageH = mapImageH * 2
        scaleH = scaleH + 1
      }
      if (scaleH > scaleW) {
        minZoom = scaleH - 1
      } else {
        minZoom = scaleW - 1
      }
    } else {
      let scaleW = 1
      while (mapImageW / (radio * 2) > mapDivW) {
        mapImageW = mapImageW / 2
        scaleW = scaleW + 1
      }
      let scaleH = 1
      while (mapImageH / (radio * 2) > mapDivH) {
        mapImageH = mapImageH / 2
        scaleH = scaleH + 1
      }
      if (scaleH > scaleW) {
        minZoom = -1 * scaleH - 1
      } else {
        minZoom = -1 * scaleW - 1
      }
    }
  }

  const center = xy(map.imageMapWidth / 2, map.imageMapHeight / 2)
  //需要按照图片的wh设置地图的setMaxBounds
  const maxBounds = L.latLngBounds([
    xy(0 - PaddingMap, 0 - PaddingMap),
    xy(map.imageMapWidth + PaddingMap, map.imageMapHeight + PaddingMap)
  ])
  const fitBounds = L.latLngBounds([xy(0, 0), xy(map.imageMapWidth, map.imageMapHeight)])
  let maxZoom = minZoom < 0 ? 2 : minZoom + 2
  // console.log('11111',minZoom, map.getBoundsZoom(maxBounds, true), map.getBoundsZoom(fitBounds, true))
  if (map) {
    map.setMaxBounds(maxBounds)
    map.fitBounds(fitBounds)
    map.panTo(center)
    map.invalidateSize({ animate: false })
    map.setMinZoom(minZoom)
    map.setMaxZoom(maxZoom)
    // map.setZoom(minZoom)
    map.setView(center, minZoom) //地图容器div，scale到最小时也不足以容纳image时，无法中心，加这句话可以强制中心
  }
  // setTimeout(() => {
  //   if (map) {
  //     map.panTo(center)
  //     map.invalidateSize({ animate: false })
  //     // map.setMinZoom(minZoom)
  //     // map.setMaxZoom(minZoom < 0 ? 2 : minZoom + 2)
  //     // map.setZoom(minZoom)
  //   }
  // }, 10)
}

/**
 * full热力图可以简单的创建在map container内即可
 * 1.创建一个div，在改div内放置HeatMap canvas
 * 然后根据地图可视范围，以及zoom值，转化热力数据（热力数据必然是基于平面图的坐标生成的）
 * @param map 地图
 * @param data 热力数据（热力数据必然是基于平面图的坐标生成的）
 * @returns HTMLDivElement HeatMap canvas的容器div
 */
export function showFullHeatmap(map: L.Map, data: StoreData): HTMLDivElement | null {
  if (!map) return null
  // console.log('showFullHeatmap', data)
  const rect = map.getContainer().getBoundingClientRect()
  let div: HTMLDivElement | null = null

  if (!map.heatmap) {
    div = document.createElement(`DIV`) as HTMLDivElement
    div.style.background = 'rgb(10 9 219 / 26%)'
    div.style.position = 'absolute'
    div.style.left = '0'
    div.style.top = '0'
    div.style.width = rect.width + 'px'
    div.style.height = rect.height + 'px'
    div.style.transform = 'translate3d(0,0,0)' // 启用GPU硬件加速（transform:translate3d(0,0,0)、transform:translateZ(0)）

    map.getContainer().appendChild(div)
    // map.getContainer().appendChild(div)
    //"translate3d(0px, 0px, 0px)"
    // 重新设置imagesrc后有可能导致ele的transform的translate3d坐标变化，所以要重置
    // let translate3d = ele.style.transform
    // if (translate3d) {
    //   translate3d = translate3d.replace('translate3d(', '').replace(')', '')
    //   let arr = translate3d.split(',')
    //   div.style.left = -1 * Number(arr[0].replace('px', '')) + 'px'
    //   div.style.top = -1 * Number(arr[1].replace('px', '')) + 'px'
    // }
    div.style.zIndex = 400 + 1 + ''
    //leaflet-pane leaflet-overlay-pane 也就是图片所在div的zindex = 400
    let r = rect.width / 80
    let heatmap = new HeatMap({
      className: 'leaflet-heatmap-canvas',
      container: div,
      maxOpacity: 0.6,
      radius: r,
      blur: 0.9,
      width: rect.width,
      height: rect.height,
      // backgroundColor: 'red',
      data: data
    })
    map.heatmap = heatmap
  }
  div = map.heatmap.config.container
  refreshFullHeatmapData(map, data)
  return div
}
/**
 * 刷新full热力图
 * 热力数据转换规则
 * 1.获取地图最大范围的getSouthWest，
 * 2.计算显示比例ratio = Math.pow(2, map.getZoom())
 * 3.热力数据x = (x - sw.lng) * ratio
 * 4.热力数据y = (y - sw.lat) * ratio
 * @param map 地图
 * @param data 热力数据（热力数据必然是基于平面图的坐标生成的，该数据需要转化）
 * @returns
 */
export function refreshFullHeatmapData(map: L.Map, data: StoreData) {
  if (!map || !map.heatmap) return
  let ratio = Math.pow(2, map.getZoom())
  let bounds = map?.getBounds()
  let heatmapdata: StoreData = { max: data.max, data: [] }
  let sw: L.LatLng = bounds.getSouthWest()

  data.data.forEach((item) => {
    heatmapdata.data.push({
      x: Math.ceil((item.x - sw.lng) * ratio),
      y: Math.ceil((item.y - sw.lat) * ratio),
      value: item.value
    })
  })

  map.heatmap.setData(heatmapdata)
}



export const popDistance: L.Popup = L.popup({
  closeButton: false,
  closeOnClick: false,
  autoClose: false
}).setContent(document.createElement('DIV'))

function getMoveMarker(
  map: L.Map,
  markMove1: L.Marker,
  markMove2: L.Marker,
  pos1: L.LatLngExpression,
  pos2: L.LatLngExpression
): L.Polyline {
  const polylineDistance: L.Polyline = new L.Polyline(
    [markMove1.getLatLng(), markMove2.getLatLng()],
    {
      color: 'red',
      weight: 2,
      opacity: 0.9
      // pmIgnore: true,
    }
  ) //用于距离线

  markMove1.addTo(map)
  markMove1.setLatLng(pos1)
  markMove1.on('mouseover', function () {})

  markMove2.addTo(map)
  markMove2.setLatLng(pos2)
  markMove2.on('mouseover', function () {})

  return polylineDistance
}

export function testDistanceVNode(
  map: L.Map,
  component: VNode,
  pixelsCallback?: (
    pixels: number,
    mark1: L.Marker,
    mark2: L.Marker,
    polylineDistance: L.Polyline
  ) => void,
  markMove1?: L.Marker,
  markMove2?: L.Marker
) {
  try {
    // ----------------------------------------------------------------
    // 以下为move相关
    if (markMove1 == null || markMove1 == undefined) {
      markMove1 = L.marker(xy(0, 0), {
        icon: L.icon({
          iconUrl: '/resource/svg/point.svg',
          className: 'mark-move-icon',
          iconSize: [15, 15] // size of the icon
        }),
        draggable: true
        // pmIgnore: true,  //这个在使用pm时，要打开
      })
    }
    if (markMove2 == null || markMove2 == undefined) {
      markMove2 = L.marker(xy(0, 0), {
        icon: L.icon({
          iconUrl: '/resource/svg/point.svg',
          className: 'mark-move-icon',
          iconSize: [15, 15] // size of the icon
        }),
        draggable: true
        // pmIgnore: true,  //这个在使用pm时，要打开
      })
    }
    let polylineDistance = getMoveMarker(
      map,
      markMove1,
      markMove2,
      xy(map.imageMapWidth, map.imageMapHeight / 2),
      xy(0, map.imageMapHeight / 2)
    )
    polylineDistance.setLatLngs([markMove1.getLatLng(), markMove2.getLatLng()])

    const center = getCenterPoint(markMove1.getLatLng(), markMove2.getLatLng())
    popDistance.setLatLng(center)
    map.addLayer(polylineDistance)

    let div = document.createElement('DIV') as HTMLDivElement
    popDistance.setContent(div)
    render(component, div)
    popDistance.openOn(map)
    const mks = document.getElementsByClassName('mark-move-icon')
    Array.prototype.forEach.call(mks, (element) => {
      element.style.cursor = 'move'
    })

    const dragEvent = function (_e: L.LeafletEvent) {
      if (!map) return
      const posNew = _e.target.getLatLng()
      let newX = posNew.lng
      let newY = posNew.lat
      if (newX < 0) newX = 0
      if (newY < 0) newY = 0
      if (newX >= map.imageMapWidth) newX = map.imageMapWidth
      if (newY >= map.imageMapHeight) newY = map.imageMapHeight
      _e.target.setLatLng(xy(newX, newY))
      polylineDistance.setLatLngs([markMove1!.getLatLng(), markMove2!.getLatLng()])

      const center = getCenterPoint(markMove1!.getLatLng(), markMove2!.getLatLng())
      popDistance.setLatLng(center)

      if (pixelsCallback) {
        let dis = getDisance(markMove1!.getLatLng(), markMove2!.getLatLng())
        pixelsCallback(dis, markMove1!, markMove2!, polylineDistance)
      }
    }

    markMove1.on('drag', dragEvent)
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    markMove1.on('dragend', function (_e) {})
    markMove2.on('drag', dragEvent)
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    markMove2.on('dragend', function (_e) {})

    if (pixelsCallback) {
      var dis = getDisance(markMove1.getLatLng(), markMove2.getLatLng())
      pixelsCallback(dis, markMove1, markMove2, polylineDistance)
    }
    // return { mark1: markMove1, mark2: markMove2, polylineDistance: polylineDistance }
  } catch (error) {
    console.error(error)
    // return null
  }
}
export function testDistanceMarkDiv(
  map: L.Map,
  markMove1?: L.Marker,
  markMove2?: L.Marker,
  div?: HTMLDivElement,
  saveFn?: (div: HTMLDivElement, disM: string, callback: (val: string) => void) => void
): { mark1: L.Marker; mark2: L.Marker; polylineDistance: L.Polyline } | null {
  try {
    // ----------------------------------------------------------------
    // 以下为move相关
    if (!markMove1)
      markMove1 = L.marker(xy(0, 0), {
        icon: L.icon({
          iconUrl: '/resource/svg/point.svg',
          className: 'mark-move-icon',
          iconSize: [15, 15] // size of the icon
        }),
        draggable: true
        // pmIgnore: true,  //这个在使用pm时，要打开
      })
    if (!markMove2)
      markMove2 = L.marker(xy(0, 0), {
        icon: L.icon({
          iconUrl: '/resource/svg/point.svg',
          className: 'mark-move-icon',
          iconSize: [15, 15] // size of the icon
        }),
        draggable: true
        // pmIgnore: true,  //这个在使用pm时，要打开
      })

    let polylineDistance = getMoveMarker(
      map,
      markMove1,
      markMove2,
      xy(map.imageMapWidth, map.imageMapHeight),
      xy(0, 0)
    )
    polylineDistance.setLatLngs([markMove1.getLatLng(), markMove2.getLatLng()])

    const center = getCenterPoint(markMove1.getLatLng(), markMove2.getLatLng())
    popDistance.setLatLng(center)
    map.addLayer(polylineDistance)
    if (!div) {
      const title = `<div style="text-align:center; padding-top:10px;padding-bottom:8px;font-size:14px;font-weight: bold;color:red"> proportion </div>`
      const btnSaveHtml = `<button  id="__btnSaveDistance" class="ant-btn ant-btn-primary" type="button">${yesBtnSvg}</button>`
      const inputPrefixHTML = '<font color="#0960bd">' + 123 + '&nbsp;' + 'pixel' + '&nbsp;=</font>'
      const content =
        title +
        `<div style="display: flex;" >` +
        ` <span class="ant-input-group-wrapper css-dev-only-do-not-override-19yxfbp">` +
        `    <span class="ant-input-wrapper ant-input-group css-dev-only-do-not-override-19yxfbp">` +
        `       <span class="ant-input-group-addon">${inputPrefixHTML}</span>` +
        `       <input id="__inputDistance" style="width: 80px;" type="text" class="ant-input css-dev-only-do-not-override-19yxfbp">` +
        `       <span class="ant-input-group-addon">YYYY</span>` +
        `    </span>` +
        ` </span>` +
        btnSaveHtml +
        '</div> '
      div = document.createElement('DIV') as HTMLDivElement
      div.innerHTML = content
    }

    popDistance.setContent(div)

    popDistance.openOn(map)
    const mks = document.getElementsByClassName('mark-move-icon')
    Array.prototype.forEach.call(mks, (element) => {
      // 对当前元素进行操作
      element.style.cursor = 'move'
    })

    setTimeout(() => {
      const btnSave = document.getElementById('__btnSaveDistance') as HTMLElement
      const distanceTxt = document.getElementById('__inputDistance') as HTMLInputElement
      if (btnSave) {
        let isLoading = false
        btnSave.addEventListener('click', (_e) => {
          console.log('btnSave click', isLoading)
          if (isLoading) {
            return
          }
          // let disMm = Number(distanceTxt.value)
          if (saveFn) {
            btnSave.innerHTML = loadingBtnSvg
            _e.preventDefault()
            _e.stopPropagation()
            isLoading = true
            saveFn(div!, distanceTxt.value, (data: string) => {
              console.log('saveFn:' + data)
              btnSave.innerHTML = yesBtnSvg
              isLoading = false
            })
          } else {
          }
        })
      }
    }, 100)
    return { mark1: markMove1, mark2: markMove2, polylineDistance: polylineDistance }
  } catch (error) {
    console.error(error)
    return null
  }
}
